/* 
   Copyright(C)2012 Fredy Ramirez - www.pacificcoolwave.com
 
   This file is part of the TerraCool project.
	
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Threading;
using System.Xml;
using System.Globalization;
using System.IO;

namespace Pcw.TerraCool
{
	/// <summary>
	/// Catalog.
	/// </summary>
	public class Catalog
	{
		// Header
		string author_neutral = null;
		string url_neutral = null;
		SourceType type_neutral = SourceType.NotDefined;
		string locale_neutral = null;
		string author_specific = null;
		string url_specific = null;
		SourceType type_specific = SourceType.NotDefined;
		string locale_specific = null;

		CultureInfo info;
		string package_name = null;
		string locale_dir = null;
		CatalogValues values = null;

		/// <summary>
		/// Initializes a new instance of the <see cref="Pcw.TerraCool.Catalog"/> class.
		/// </summary>
		/// <param name='neutralXmlLocale'>Neutral xml locale.</param>
		/// <param name='specificXmlLocale'>Specific xml locale.</param>
		public Catalog (XmlDocument neutralXmlLocale, XmlDocument specificXmlLocale) 
		{
			GetLocaleStrings(neutralXmlLocale, specificXmlLocale);
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="Pcw.TerraCool.Catalog"/> class.
		/// </summary>
		/// <param name='packageName'>Package name.</param>
		/// <param name='localeDir'>Locale dir.</param>
		public Catalog (string packageName, string localeDir)
		{
			GetLocaleStrings(GetNeutralXml(packageName, localeDir, Thread.CurrentThread.CurrentCulture), 
			                 GetSpecificXml());
		}

		/// <summary>
		/// Gets or sets the catalog culture info.
		/// </summary>
		/// <value>
		/// The catalog culture info.
		/// </value>
		public CultureInfo CatalogCultureInfo {
			get {
				return info;
			}
			set {
				if (info != null) {
					CultureInfo inf = value;
					if (inf != null && inf.Name != info.Name) {
						GetLocaleStrings(GetNeutralXml(package_name, 
						                               locale_dir, inf), 
						                 				GetSpecificXml());
					}
				}
			}
		}

		public string AuthorNeutral {get {return author_neutral;}}
		public string UrlNeutral {get {return url_neutral;}}
		public string LocaleNeutral {get {return locale_neutral;}}
		public SourceType SourceTypeNeutral {get {return type_neutral;}}
		public string AuthorSpecific {get {return author_specific;}}
		public string UrlSpecific {get {return url_specific;}}
		public string LocaleSpecific {get {return locale_specific;}}
		public SourceType SourceTypeSpecific {get {return type_specific;}}

		/// <summary>
		/// Gets the translated string.
		/// </summary>
		/// <returns>The translated string.</returns>
		/// <param name='strId'>String to translate.</param>
		public string GetString(string strId) 
		{
			if (values==null)
				return strId;
			string res = values.GetSingle(strId);
			//Console.WriteLine (" Returning: {0}", res);
			return res;
		}

		/// <summary>
		/// Gets the translated plural string.
		/// </summary>
		/// <returns>
		/// The plural or single translated string.
		/// </returns>
		/// <param name='singleString'>Single string.</param>
		/// <param name='pluralString'>Plural string.</param>
		/// <param name='n'>Integer that determines which string to process (single or plural). 
		/// If 'n' is less than one it uses single, otherwise plural.
		/// </param>
		public string GetPluralString(string singleString, string pluralString, int n) 
		{
			if (values == null) {
				if (n <= 1)
					return singleString;
				else
					return pluralString;
			}

			if (n <= 1)
				return GetString(singleString);
			else 
				return values.GetPlural(singleString, pluralString);
		}

		private void AddStringsFromXml (XmlDocument xml, bool isNeutral) 
		{
			if (xml == null)
				return;

			try {
				XmlNode xNode;
				xNode = xml.LastChild;
				if (xNode == null || xNode.Name != "TerraCool")
					throw new XmlException  ("Error: XML locale document does not contain the 'TerraCool' root element.");

				foreach(XmlNode xNodeLoop in xNode.ChildNodes)
				{
					if (xNodeLoop.NodeType != XmlNodeType.Element)
						continue;

					// GetHeader
					if(xNodeLoop.Name == "Header")
					   GetHeader(xNodeLoop, isNeutral); 

					// Strings
					if(xNodeLoop.Name == "Strings") {
						if (values==null)
							values = new CatalogValues();
						values.AddStringsTranslated(xNodeLoop);
					}
				}
			}
			catch (Exception e) 
			{
				throw new InvalidOperationException("Error while trying to get strings from locale. " + e.Message);
			}
		}

		private void GetHeader (XmlNode xNodeLoop, bool isNeutral) 
		{
			string author = null;
			string url = null; 
			string locale = null;
			string name = null;
			string text = null;
			SourceType type = SourceType.NotDefined;

			foreach(XmlNode str in xNodeLoop.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;

				name = str.Name;
				text = str.InnerText;

				if(name == "Author")
					author = text;
				else if (name == "Url")
					url = text;
				else if (name == "Locale")
					locale = text;
				else if (name == "Type")
					if (Enum.IsDefined(typeof(SourceType), text)) 
					    type = (SourceType) Enum.Parse(typeof(SourceType), text);
			}

			if (isNeutral){
				author_neutral = author;
				url_neutral = url;
				locale_neutral = locale;
				type_neutral = type;
			}
			else {
				author_specific = author;
				url_specific = url;
				locale_specific = locale;
				type_specific = type;
			}
		}

		private XmlDocument GetNeutralXml(string packageName, string localeDir, CultureInfo cultureInfo) 
		{
			package_name = packageName.Trim();
			info = cultureInfo;
			values = null;
			locale_dir = null;

			if (!Directory.Exists(localeDir))
				return null;
			locale_dir = localeDir;

			if (!locale_dir.EndsWith(Path.DirectorySeparatorChar.ToString())) 
				locale_dir = locale_dir + Path.DirectorySeparatorChar.ToString();

			locale_dir = locale_dir + "locale" + Path.DirectorySeparatorChar.ToString();

			if (!Directory.Exists(localeDir)) {
				locale_dir = null;
				return null;
			}

			return GetXmlDocument(info.TwoLetterISOLanguageName);

		}

		private XmlDocument GetSpecificXml() 
		{
			if (locale_dir == null)
				return null;
			if (info.Name == info.TwoLetterISOLanguageName) // It is necessary?
				return null;;
			return GetXmlDocument(info.Name);
		}

		private XmlDocument GetXmlDocument(string localeCode) 
		{
			string file = Path.Combine(locale_dir + localeCode, package_name + ".xml");
			if (!File.Exists(file))
				return null;

			XmlDocument xml = new XmlDocument();
			try {
				xml.Load(file);
			}
			catch (Exception e) 
			{
				throw new InvalidOperationException("Error while trying to get xml document from locale file " + file + ". " + e.Message);
			}
			return xml;
		}

		private void GetLocaleStrings(XmlDocument neutralXmlLocale, XmlDocument specificXmlLocale)
		{
			AddStringsFromXml(neutralXmlLocale, true);
			AddStringsFromXml(specificXmlLocale, false);
		}
	} 
}

